/*
 * PipelineCache.java
 *
 * Created on December 21, 2004, 3:16 PM
 */

package org.infoset.xproc;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.infoset.component.Component;
import org.infoset.component.ComponentRegistry;
import org.infoset.xml.XMLException;

/**
 *
 * @author R. Alexander Milowski
 */
public abstract class PipelineCache
{

   static class UseRecord {
      Component pipe;
      boolean inUse;
      long leased;
      UseRecord(Component pipe) {
         this.pipe = pipe;
         this.inUse = false;
         this.leased = 0;
      }
   }
   boolean checkFirst;
   Map<URI,Pipeline> pipelines;
   Map<URI,List<UseRecord>> cache;
   /** Creates a new instance of PipelineCache */
   public PipelineCache()
   {
      this(false);
   }
   
   public PipelineCache(boolean checkForChanges)
   {
      this.checkFirst = checkForChanges;
      pipelines = new HashMap<URI,Pipeline>();
      cache = new HashMap<URI,List<UseRecord>>();
   }
   
   public boolean isCheckingForChanges() {
      return checkFirst;
   }
   
   public void setCheckForChanges(boolean flag) {
      checkFirst = flag;
   }
   
   public Component getInstance(URI id) 
      throws XMLException
   {
      if (checkFirst && !isUpToDate(id)) {
         synchronized (cache) {
            cache.remove(id);
            pipelines.remove(id);
         }
      }
      List<UseRecord> useList = null;
      synchronized (cache) {
         useList = cache.get(id);
      }
      if (useList==null) {
         useList = new ArrayList<UseRecord>();
         Pipeline pipe = loadPipelineInstance(id);
         synchronized(pipelines) {
            pipelines.put(id,pipe);
         }
         synchronized (cache) {
            cache.put(id,useList);
         }
      }
      for (Iterator pipes=useList.iterator(); pipes.hasNext(); ) {
         UseRecord record = (UseRecord)pipes.next();
         synchronized (record) {
            if (!record.inUse) {
               record.inUse = true;
               record.leased = System.currentTimeMillis();
               return record.pipe;
            }
         }
      }
      Pipeline pipe = pipelines.get(id);
      UseRecord record = new UseRecord(pipe.getDefinition().newInstance());
      record.inUse = true;
      record.leased = System.currentTimeMillis();

      synchronized (useList) {
         useList.add(record);
      }
      return record.pipe;
   }
   
   public void release(URI id, Component instance) {
      List useList = null;
      synchronized (cache) {
         useList = (List)cache.get(id);
      }
      if (useList!=null) {
         for (Iterator pipes=useList.iterator(); pipes.hasNext(); ) {
            UseRecord record = (UseRecord)pipes.next();
            synchronized (record) {
               if (record.pipe==instance) {
                  record.inUse = false;
                  return;
               }
            }
         }
      }
   }

   protected abstract boolean isUpToDate(URI id);
   
   protected abstract Pipeline loadPipelineInstance(URI id)
      throws XMLException;
   
   public abstract void setComponentRegistry(ComponentRegistry registry);
   
}
